package com.bvbot.core.bot;

import static com.bvbot.core.BvBotRuntimeException.exit;
import static com.bvbot.core.Logger.log;

import com.bvbot.core.extractor.BvExtractor;
import com.bvbot.core.extractor.KitcoExtractor;
import com.bvbot.core.order.SecurityBuyer;
import com.bvbot.core.order.SecuritySeller;

public abstract class AbstractBot<T extends AbstractBotConfig> extends Thread {

	private static final long DEFAULT_ROBOT_PAUSE_IN_MS = 3000;

	private static final long DEFAULT_ROBOT_ERROR_PAUSE_IN_MS = 60000;

	private T config = null;

	private PricesHistory history;

	private boolean workToDo = true;

	private BvExtractor bvExtractor;

	public AbstractBot() {
	}

	public AbstractBot(T config) {
		if (config == null) {
			exit("Incorrect parameters given");
		}
		this.config = config;
		history = new PricesHistory();
		bvExtractor = new BvExtractor();
	}

	public T getConfig() {
		return config;
	}

	public void setConfig(T config) {
		this.config = config;
	}

	public abstract Decision computeDecision();

	protected void askForTermination() {
		log("Asking for termination");
		workToDo = false;
	}

	@Override
	public void run() {
		Thread.currentThread().setName(getNameForThread());
		beforeRun();
		while (workToDo) {
			try {
				Prices pri = getCurrentSecurityiesPrice();
				if (pri != null) {
					history.addCurrentPrice(pri);
					newCurrentPriceExtracted(pri);
					Decision decision = computeDecision();
					applyDecision(decision);
				} else {
					log("No price extracted. Will try next time.");
				}
				Thread.sleep(getPauseInMillis());
			} catch (Throwable t) {
				log("Throwable encountered : " + t.getMessage(), t);
				doWait(DEFAULT_ROBOT_ERROR_PAUSE_IN_MS);
				reinitState();
			}
		}
		log("End of Bot application.");
	}

	protected String getNameForThread() {
		return "VolatilityBot";
	}

	protected abstract void reinitState();

	private void doWait(long defaultRobotErrorPauseInMs) {
		try {
			Thread.sleep(defaultRobotErrorPauseInMs);
		} catch (InterruptedException e) {
			log("Exception while waiting : " + e.getMessage(), e);
		}
	}

	protected void newCurrentPriceExtracted(Prices currentPrice) {
	}

	protected abstract void applyDecision(Decision decision);

	protected void beforeRun() {
	}

	public long getPauseInMillis() {
		return DEFAULT_ROBOT_PAUSE_IN_MS;
	}

	protected Prices getCurrentSecurityiesPrice() {
		Prices pri = extractSecuritesPrice();
		return pri;
	}

	@SuppressWarnings("unused")
	private void sortirSurErreur(String message, Throwable t) {
		log("Sortie sur erreur : " + message, t);
		workToDo = false;
	}

	private Prices extractSecuritesPrice() {
		Prices result = bvExtractor.extract();
		return result;
	}

	@SuppressWarnings("unused")
	private Float extractKitcoGoldPrice() {
		KitcoExtractor extractor = new KitcoExtractor();
		Float extractedPrice = extractor.extract();
		return extractedPrice;
	}

	protected void sellGold() {
		SecuritySeller seller = new SecuritySeller(true);
		seller.sellRatio(1);
	}

	protected void sellSilver() {
		SecuritySeller seller = new SecuritySeller(false);
		seller.sellRatio(1);
	}

	protected void buyGold() {
		SecurityBuyer buyer = new SecurityBuyer(true);
		buyer.buyGrams(20);
	}

	protected void buySilver() {
		SecurityBuyer buyer = new SecurityBuyer(false);
		buyer.buyGrams(100);
	}

	public PricesHistory getHistory() {
		return history;
	}

}
